package com.zzyy.study.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyy.study.entities.User;
import com.zzyy.study.mapper.UserMapper;
import com.zzyy.study.service.UserService;
import com.zzyy.study.service.UserTXService;
import jakarta.annotation.Resource;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @auther zzyy
 * @create 2024-07-03 15:11
 * 同一个类中方法嵌套调用，正确使用下口诀
 * 1 @Transactional加外不加内
 * 或者
 * 2 @Transactional内外都加
 * 或者
 * 3 加内没加外,AopContext处理+开启@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
 *
 *

    多线程异常
    手动回滚

 */
@Service(value = "userTXServiceA")
public class UserTXServiceImpl extends ServiceImpl<UserMapper, User> implements UserTXService
{
    @Resource
    private UserMapper userMapper;

    /**
     * V1 加外没加内
     * 外部方法addtx且自带Transactional，调用本类方法内部updateUserPwd且内部方法出错
     * 结论：内外都会回滚
     * @param user
     * @return
     */
    /*@Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addtx(User user)
    {
        //1 新增
        userMapper.insert(user);
        //2 修改1号记录的用户密码
        this.updateUserPwd(1);

        return true;
    }
    public int updateUserPwd(Integer id)
    {
        User oneUser = userMapper.selectById(id);

        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(User::getPassword,RandomUtil.randomString(6))
                .set(User::getVersion,oneUser.getVersion()+1)
                .eq(User::getId,id)
                .eq(User::getVersion,oneUser.getVersion());

        userMapper.update(updateWrapper);
        int age = 10/0;
        return 1;
    }*/

    /**
     * V2 加外没加内
     * 外部方法addtx且自带Transactional且外部方法出错，调用本类方法内部updateUserPwd
     * 结论：内外都会回滚
     * @param user
     * @return
     */
    /*@Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addtx(User user)
    {
        //1 新增
        userMapper.insert(user);
        //2 修改1号记录的用户密码
        this.updateUserPwd(1);

        int age = 10/0;
        return true;
    }
    public int updateUserPwd(Integer id)
    {
        User oneUser = userMapper.selectById(id);

        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(User::getPassword,RandomUtil.randomString(6))
                .set(User::getVersion,oneUser.getVersion()+1)
                .eq(User::getId,id)
                .eq(User::getVersion,oneUser.getVersion());

        userMapper.update(updateWrapper);

        return 1;
    }*/


    /**
     * V3 加内没加外
     * 外部方法addtx且外部方法出错，调用本类方法内部updateUserPwd且自带Transactional
     * 结论：控制不住，事务回滚失败
     * @param user
     * @return
     */
    /*@Override
    public boolean addtx(User user)
    {
        //1 新增
        userMapper.insert(user);
        //2 修改1号记录的用户密码
        this.updateUserPwd(1);

        int age = 10/0;
        return true;
    }
    @Transactional(rollbackFor = Exception.class)
    public int updateUserPwd(Integer id)
    {
        User oneUser = userMapper.selectById(id);

        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(User::getPassword,RandomUtil.randomString(6))
                .set(User::getVersion,oneUser.getVersion()+1)
                .eq(User::getId,id)
                .eq(User::getVersion,oneUser.getVersion());

        userMapper.update(updateWrapper);

        return 1;
    }*/

    /**
     * V4 加内没加外
     * 外部方法addtx，调用本类方法内部updateUserPwd且自带Transactional且内部方法出错
     * 结论：控制不住，事务回滚失败
     * @param user
     * @return
     */
    /*@Override
    public boolean addtx(User user)
    {
        //1 新增
        userMapper.insert(user);
        //2 修改1号记录的用户密码
        this.updateUserPwd(1);
        return true;
    }
    @Transactional(rollbackFor = Exception.class)
    public int updateUserPwd(Integer id)
    {
        User oneUser = userMapper.selectById(id);

        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(User::getPassword,RandomUtil.randomString(6))
                .set(User::getVersion,oneUser.getVersion()+1)
                .eq(User::getId,id)
                .eq(User::getVersion,oneUser.getVersion());

        userMapper.update(updateWrapper);

        int age = 10/0;
        return 1;
    }*/

    /**
     * V5 内外两个都加
     * 两个方法都加了@Transactional，不管是外层方法addtx还是内层方法updateUserPwd出错了，都会回滚
     * 结论：内外都会回滚
     * @param user
     * @return
     */
    /*@Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addtx(User user)
    {
        //1 新增
        userMapper.insert(user);
        //2 修改1号记录的用户密码
        this.updateUserPwd(1);
        int age = 10/0;
        return true;
    }
    @Transactional(rollbackFor = Exception.class)
    public int updateUserPwd(Integer id)
    {
        User oneUser = userMapper.selectById(id);

        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(User::getPassword,RandomUtil.randomString(6))
                .set(User::getVersion,oneUser.getVersion()+1)
                .eq(User::getId,id)
                .eq(User::getVersion,oneUser.getVersion());

        userMapper.update(updateWrapper);

        //int age = 10/0;
        return 1;
    }*/


    /**
     * V6 加内没加外,AopContext处理
     * 结论：内外都会回滚
     * @param user
     * @return
     */
    /*@Override
    public boolean addtx(User user)
    {
        ((UserTXServiceImpl)AopContext.currentProxy()).insertAndUpdateUserPwd(user);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public void insertAndUpdateUserPwd(User user)
    {
        //1 新增
        userMapper.insert(user);
        //2 修改1号记录
        User oneUser = userMapper.selectById(1);

        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(User::getPassword,RandomUtil.randomString(6))
                .set(User::getVersion,oneUser.getVersion()+1)
                .eq(User::getId,oneUser.getId())
                .eq(User::getVersion,oneUser.getVersion());

        userMapper.update(updateWrapper);

        //int age = 10/0;
    }*/



    /**
     * Case1
     * 异常被内部catch块吞掉了，异常后事务回滚失败，记得将异常抛出，让最外层的事务感知到
     * 1.1 catch块添加，throw new RuntimeException("addCustomer1报告异常:"+e.getMessage());
     * 1.2 catch块添加，TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
     *
     * @param customer
     */
    /*@Override
    @Transactional
    public void addCustomer1(Customer customer)
    {
        try
        {
            int i = customerMapper.insert(customer);
            int age = 10 / 0;
        } catch (Exception e) {
            e.printStackTrace();
            //throw new RuntimeException("addCustomer1报告异常:"+e.getMessage()); //OK
            TransactionInterceptor.currentTransactionStatus().setRollbackOnly();// 手动回滚
        }
    }*/

    /**
     * Case4
     * 4.1 父线程异常，子线程正常：
     * 父线程抛出异常，主线程事务回滚，
     * 由于子线程是独立存在和父线程不在同一个事务中(案中案，局中局)，
     * 所以子线程的修改并不会被回滚，会插入数据库
     *
     * 多线程环境下，内外方法是两个事务，事务具有隔离性，事务之间不会互相干扰。
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public boolean addtx(User user)
    {
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
        {
            @Override
            public void uncaughtException(Thread t, Throwable e)
            {
                // 在此可以处理未被捕获的线程异常
                System.out.println("###################线程 " + t.getName() + " 发生了异常: " + e.getMessage());
            }
        });


        //子线程正常，修改1号记录
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                System.out.println(Thread.currentThread().getName() + "\t" + "操作开始");
                //2 修改1号记录
                User oneUser = userMapper.selectById(1);

                LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper
                        .set(User::getPassword,RandomUtil.randomString(6))
                        .set(User::getVersion,oneUser.getVersion()+1)
                        .eq(User::getId,oneUser.getId())
                        .eq(User::getVersion,oneUser.getVersion());

                userMapper.update(updateWrapper);
                System.out.println(Thread.currentThread().getName() + "\t" + "操作完成");
                int son = 2 / 0; //子线程异常

            }
        }, "sonThread").start();


        //1 新增
        userMapper.insert(user);

        try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }

        //int age = 10 / 0; //父线程异常

        return true;
    }




    /**
     * Case4
     * 4.2 子线程的异常不会被外部的线程捕获，所以父线程不抛异常，事务回滚没有生效。
     */
    /*@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void addCustomer42(Customer customer) throws Exception
    {

        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                customerMapper.insert(customer);
                int age = 10/0;
            }
        },"sonThread").start();

        try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }

        System.out.println(Thread.currentThread().getName()+"\t"+"----主线程未报异常，到此一游，^_^");
    }*/




}
